/* global util: false, jNs: false, Module: false */
"use strict";
var Namespace = (function(jNs, util) {
    function Namespace(name) {
        var priv = {
            name: name,
            modules: {}
        };

        Object.defineProperty(this, "name", {
            value: priv.name,
            configurable: false,
            writable: false
        });

        Object.defineProperty(this, "modules", {
            configurable: false,
            get: function() {
                var names = [];
                for (var name in priv.modules) {
                    names.push(name);
                }
                return names;
            }
        });

        this.getModule = function(name) {
            return priv.modules[name];
        };

        this.putModule = function(module) {
            if (!(module instanceof Module)) {
                throw new Error("[jNs] putModule need a Module instance");
            }

            var name = module.name;
            if (!name) {
                return;
            }

            if (priv.modules[name]) {
                if (priv.modules[name] === module) {
                    // 同一个 module 已经放入，忽略即可
                    return;
                }

                throw new Error("[jNs] duplicate module [" + name + "]");
            }
            priv.modules[name] = module;
        };
    }

    (function(fn) {
        fn.using = function(name) {
            return jNs(this.name, name);
        };
        fn.define = function(name, factory) {
            if (util.isFunction(name)) {
                factory = name;
                name = undefined;
            }

            var m = util.splitFullName(name || "");
            var ns = this.using(m.ns);

            if (m.name && ns.getModule(m.name)) {
                throw new Error([
                    "[jNs] module [",
                    m.name,
                    "] in namespace [",
                    ns.name,
                    "] is already defined."
                ].join(""));
            }

            var module = new Module(ns, m.name, factory);
            if (m.name) {
                ns.putModule(module);
            }
            return module;
        };

        fn.use = (function() {
            function use(fn, name, callback) {
                var m = util.splitFullName(name);
                var ns = fn.using(m.ns);
                var module = ns.getModule(m.name);
                if (!module) {
                    throw new Error([
                        "[jNs] module [",
                        m.name,
                        "] in namespace [",
                        ns.name,
                        "] is not defined."
                    ].join(""));
                }
                return module.use(callback);
            }

            function useArray(fn, names, callback) {
                var exports = {};
                names.forEach(function(name) {
                    exports[name] = use.call(fn, name);
                });

                if (util.isFunction(callback)) {
                    callback(exports);
                }

                return exports;
            }

            return function(name, callback) {
                return Array.isArray(name) ? useArray(this, name, callback) : use(this, name, callback);
            };
        })();

        fn.$ = function(name, factory) {
            if (util.isFunction(name) || typeof factory !== "undefined") {
                return this.define(name, factory);
            } else {
                return this.use(name);
            }
        };
    })(Namespace.prototype);

    return Namespace;
})(jNs, util);
